package com.pig4cloud.pigx.strategy.executor.contract.order;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.okex.open.api.exception.APIException;
import com.pig4cloud.pigx.common.customize.exception.ApiRequestException;
import com.pig4cloud.pigx.common.customize.exception.MqAckException;
import com.pig4cloud.pigx.common.customize.exception.MqAckLogException;
import com.pig4cloud.pigx.common.customize.exception.DuckweedException;
import com.pig4cloud.pigx.common.customize.mysql.utils.MysqlUtil;
import com.pig4cloud.pigx.common.customize.redis.utils.RedisUtils;
import com.pig4cloud.pigx.common.seata.utils.SeataUtils;
import com.pig4cloud.pigx.strategy.bo.StrategyTaskContractCacheBO;
import com.pig4cloud.pigx.strategy.bo.StrategyTaskContractCalculateProfitBO;
import com.pig4cloud.pigx.strategy.bo.okx.PlaceOrderBO;
import com.pig4cloud.pigx.strategy.bo.okx.SyncOrderBO;
import com.pig4cloud.pigx.strategy.constant.StrategyMqConstant;
import com.pig4cloud.pigx.strategy.constant.StrategyRedisLockConstant;
import com.pig4cloud.pigx.strategy.dto.StrategySyncOrderMsgDTO;
import com.pig4cloud.pigx.strategy.entity.StrategyContract;
import com.pig4cloud.pigx.strategy.entity.StrategyGroupContract;
import com.pig4cloud.pigx.strategy.entity.StrategyOrderContract;
import com.pig4cloud.pigx.strategy.util.StrategyContractUtil;
import com.pig4cloud.pigx.system.entity.SystemProduct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * 策略-合约 订单处理器的管理器
 *
 * @since 2022/11/28 10:24
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class StrategyOrderContractHandlerManager {

    public static final List<StrategyOrderContractHandler> HANDLER_LIST = new ArrayList<>(1);

    private final MysqlUtil mysqlUtil;
    private final RedisUtils redisUtils;
    private final StrategyContractUtil strategyContractUtil;

    /**
     * 获得合适的处理器
     *
     * @param platformId 平台id
     * @return {@link StrategyOrderContractHandler}
     * @since 2022/12/2 16:55
     */
    public static StrategyOrderContractHandler getHandler(Long platformId) {

        for (StrategyOrderContractHandler i : HANDLER_LIST) {
            if (i.support(platformId)) {
                return i;
            }
        }

        throw new MqAckException("未能寻找到对应平台的订单处理器：" + platformId);

    }

    /**
     * 获取USDT余额
     *
     * @param userId     用户id
     * @param platformId 平台id
     * @return {@link BigDecimal}
     * @since 2022/12/2 16:48
     */
    public BigDecimal accountBalance(Long userId, Long platformId) {
        try {
            StrategyOrderContractHandler handler = getHandler(platformId);
            return handler.accountBalance(userId);

        } catch (APIException e) {
            throw e;

        } catch (Throwable e) {
            strategyContractUtil.logApiErrorMsg("accountBalance",
                    e,
                    userId,
                    platformId);

            throw new MqAckException("未能查询到USDT额度");
        }
    }

    /**
     * 下单
     * <p>调用前一定要根据switchId获取分布式锁
     * <pre>
     *     // 获取分布式锁
     *     String key = String.format(StrategyRedisLockConstant.TASK_RUN, task.getSwitchId());
     *     RLock lock = redisUtils.waitLock(key, null);
     * </pre>// 将异常的止盈止损从缓存中去除
     *
     * @param boList boList
     * @since 2022/12/2 16:48
     */
    public void placeOrder(List<PlaceOrderBO> boList) {
        PlaceOrderBO firstBo = boList.get(0);
        StrategyOrderContractHandler handler = getHandler(firstBo.getPlatformId());

        List<StrategySyncOrderMsgDTO<PlaceOrderBO>> syncDTOList = handler.placeOrder(boList);
        syncDTOList.forEach(strategyContractUtil::sendMqSyncOrder);
    }

    /**
     * 查询产品信息
     *
     * @param productName 产品名称
     * @param platformId  平台id
     * @return {@link JSONObject}
     * @since 2022/12/3 18:03
     */
    public JSONObject productIngo(String productName, Long platformId) {

        try {
            StrategyOrderContractHandler handler = getHandler(platformId);

            return handler.getProductInfo(productName);

        } catch (Throwable e) {
            throw new DuckweedException("未能查询到对应的产品信息");
        }
    }

    /**
     * 同步订单信息
     *
     * @param dto dto
     * @since 2022/12/2 17:00
     */
    @RabbitListener(queues = StrategyMqConstant.EXCHANGE_FANOUT_STRATEGY_ORDER_CONTRACT_SYNC)
    public void syncOrder(StrategySyncOrderMsgDTO<PlaceOrderBO> dto) {
        // System.err.println("开始同步订单：" + dto);

        // 获取分布式锁
        String key = String.format(StrategyRedisLockConstant.TASK_RUN, dto.getSwitchId());
        RLock taskLock = redisUtils.getLock(key, null);
        if (Objects.isNull(taskLock)) {
            // System.err.println("同步订单时，未能获取到任务分布式锁");
            // 重新发送消息
            strategyContractUtil.sendMqSyncOrder(dto);
            return;
        }

        try {
            SyncOrderBO bo = new SyncOrderBO();
            bo.setMsg(dto);
            bo.setExternalOrderId(dto.getExternalOrderId());
            bo.setOrderId(dto.getOrderId());

            // 获取订单缓存
            StrategyOrderContract order = strategyContractUtil.getStrategyOrderCache(bo.getOrderId(), false);
            bo.setOrder(order);
            // 获取任务缓存
            StrategyTaskContractCacheBO task = strategyContractUtil.getStrategyTaskCache(order.getTaskId(), false);
            bo.setTaskCache(task);
            // 获取策略缓存
            StrategyContract strategy = strategyContractUtil.getStrategyCache(task.getStrategyId(), false);
            bo.setStrategy(strategy);
            // 获取产品数据
            SystemProduct product = strategyContractUtil.getSystemProductCache(strategy.getProductId(), false);
            bo.setProduct(product);
            // 获取策略组缓存
            StrategyGroupContract group = strategyContractUtil.getStrategyGroupCache(strategy.getGroupId(), false);
            bo.setGroup(group);

            StrategyOrderContractHandler handler = getHandler(dto.getPlatformId());
            handler.syncOrder(bo);

        } catch (MqAckLogException e) {
            // 回滚分布式事务
            SeataUtils.rollbackGlobalTx();
            // 回滚本地事务
            mysqlUtil.rollbackLocalTx();
            log.error(e.getMessage());
            // 重新发送消息
            strategyContractUtil.sendMqSyncOrder(dto);

        } catch (MqAckException e) {
            // 回滚分布式事务
            SeataUtils.rollbackGlobalTx();
            // 回滚本地事务
            mysqlUtil.rollbackLocalTx();
        } catch (Throwable e) {
            e.printStackTrace();
            // 回滚分布式事务
            SeataUtils.rollbackGlobalTx();
            // 回滚本地事务
            mysqlUtil.rollbackLocalTx();
            strategyContractUtil.logApiErrorMsg("syncOrder", e, dto);
            // 重新发送消息
            strategyContractUtil.sendMqSyncOrder(dto);
        } finally {
            // 释放任务分布式锁
            redisUtils.releaseLock(taskLock);
        }
    }

    /**
     * 计算收益和收益率数据
     * <p>毛利润，不需要计算净利润
     *
     * @param task     任务信息
     * @param strategy 策略信息
     * @param product  产品信息
     * @param price    当前币对价格
     * @return {@link StrategyTaskContractCalculateProfitBO}
     * @since 2022/11/28 20:31
     */
    public StrategyTaskContractCalculateProfitBO calculateProfit(StrategyTaskContractCacheBO task,
                                                                 StrategyContract strategy,
                                                                 SystemProduct product,
                                                                 BigDecimal price) {

        StrategyOrderContractHandler handler = getHandler(strategy.getPlatformId());
        return handler.calculateProfit(task, strategy, product, price);

    }

    /**
     * 查询仓位信息
     *
     * @param userId      用户id
     * @param productName 产品名称
     * @param platformId  平台id
     * @return {@link JSONArray}
     * @since 2022/12/2 17:00
     */
    public JSONArray positions(Long userId, String productName, Long platformId,String symbolName) throws ApiRequestException {

        StrategyOrderContractHandler handler = getHandler(platformId);
        return handler.positions(userId, productName,symbolName);

    }
}
